#! /usr/bin/env python

import os
import re
# -*- coding: UTF-8 -*-
# ==============================================================================
# Filename : .py
# Purpose  : 
# Author   : Yohann Li (lyh001686@sina.com)
# Date     : 2020-07-22 14:29
# ==============================================================================
import subprocess
import sys
import time
from itertools import chain

from PyQt5.QtCore import QTimer, Qt, pyqtSignal, QThread, pyqtSlot
from PyQt5.QtGui import QIcon, QCursor, QPixmap, QKeySequence
from PyQt5.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem, QStyle, QMenu, \
    QFileDialog, QShortcut, QMainWindow

from Widgets.GoToFolder import GoToFolder
from Widgets.NotificationBox import NoteBox
from conf.configuration import (img_path, SYSD_PATH, Astro_PATH, logger, tool_path, USBFS_STDERR_Path, USBFS_STDOUT_Path,
                           terminal_color_pattern, log_folder)
from script.tcprelay import SSH

pat_os = re.compile(r"([-drwxl@])(..)[-drwxtl@+]{7,8}\s*\d*\s*[\w-]*\s*\w*\s*(\d*)\s*(\w*)\s*(\d*)\s*([\d:]*)\s*(.*)")
pat_diags = re.compile(r"([rw-]{2})[-wrx]{7}\s*(<SYMLINK>|<DIR>|[\d,]*)\s*([\d-]*\s[\d:]*)\s(.*)")
pat_diags_n18x = re.compile(r"([\d/]{10}\s*[\d:]{5,10})\s*(<SYMLINK>|<DIR>)?\s*([\d,]*)\s*(.*)")
pat_replace_link = re.compile(r" -> .*")


class TreeView(QMainWindow):
    close_signal = pyqtSignal(str)
    note_box_list = []

    def __init__(self, port_offset: int = 10000, *args, **kwargs):
        super(TreeView, self).__init__()
        self.port_offset = port_offset
        self.setObjectName('treeview')
        self._dir_list = []  # init dir nandfs results in diags mode
        self.note_box = NoteBox()
        self.note_box_list.append(self.note_box)
        self._init_ui()
        self.usbfs_running = 0
        self._goto_status = 0
        self.click_count = 0
        self.click_timer = QTimer(self)
        self.delay = 100
        self.click_timer.timeout.connect(self.click_timer_timeout)
        self.resize(800, 500)
        self.mode = kwargs['mode']
        logger.info(f"Current mode: {self.mode}")
        self.parent = kwargs['parent']
        self._setup_menu()  # 设置菜单栏
        if self.mode == 'OS':
            self.ssh = SSH(port_offset, self)  # 用来发送和读取数据
            self.ssh2 = None

    def _setup_menu(self):
        logger.debug(f"Setting menubar")
        tool_menu = QMenu('Plugins', self)
        self.menuBar().addMenu(tool_menu)
        tool_menu.addAction(self.tr('Save To My Mac'), self.save_to, 'Ctrl+Shift+S')
        get_menu = QMenu('Insert File or Folder', self)
        get_menu.addAction(self.tr('Insert File'), self.insert_file, 'Ctrl+Shift+F')
        get_menu.addAction(self.tr('Insert Folder'), self.insert_folder, 'Ctrl+Shift+D')
        tool_menu.addMenu(get_menu)
        tool_menu.addAction("Get sysdiagnose", self.get_sysdiagnose)
        tool_menu.addAction("Get BurnIn log", self.get_astro)
        tool_menu.addAction("Go To Folder ...", self.show_go_to_folder_window, "Ctrl+Shift+G")

    def _init_ui(self):
        tree = QTreeWidget()
        self.setCentralWidget(tree)
        tree.setObjectName('tree')
        tree.setHeaderLabels(('Name', 'Size', 'LastModifyTime'))
        tree.setColumnWidth(0, 600)
        tree.addTopLevelItem(QTreeWidgetItem(tree, ['/', '', '', 'd']))
        tree.topLevelItem(0).setSelected(True)
        tree.topLevelItem(0).setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_DriveHDIcon)))
        tree.itemClicked.connect(self.item_click_timer)
        tree.setExpandsOnDoubleClick(True)
        tree.setContextMenuPolicy(Qt.CustomContextMenu)
        tree.customContextMenuRequested.connect(self.r_key_menu)  # 设置右键菜单
        QShortcut(QKeySequence.Close, self).activated.connect(self.close)  # 快捷键关闭窗口
        QShortcut(QKeySequence('Ctrl+G'), self).activated.connect(self.get_subdir_index)  # 获取子项目的名称
        tree.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def r_key_menu(self):
        """右键菜单"""
        tree: QTreeWidget = self.findChild(QTreeWidget, 'tree')
        menu = QMenu(tree)
        menu.addAction(QIcon(QPixmap(os.path.join(img_path, 'save_to.png')).scaled(64, 64)), 'Save to my Mac',
                       self.save_to)
        sub_menu = menu.addMenu(QIcon(QPixmap(os.path.join(img_path, 'upload.png')).scaled(64, 64)), 'Insert to Device')
        sub_menu.addAction(QIcon(self.style().standardPixmap(QStyle.SP_FileIcon)), 'Insert file to Device',
                           self.insert_file)
        sub_menu.addAction(QIcon(self.style().standardPixmap(QStyle.SP_DirIcon)), 'Insert folder to Device',
                           self.insert_folder)
        menu.addAction(QIcon(QPixmap(os.path.join(img_path, 'trash.png')).scaled(64, 64)), 'Delete', self.del_file_folder)
        menu.show()
        menu.exec_(QCursor.pos())

    go_to_folder_window = []
    def show_go_to_folder_window(self):
        """Show Go to folder window"""
        if self.isActiveWindow():
            self.gotofolder = GoToFolder()
            self.go_to_folder_window.append(self.gotofolder)
            self.gotofolder.position_signal.connect(self.goto)
            self.gotofolder.show()

    def goto(self, dst: str):
        self.goto_status = 1 if not self.goto_status else logger.warning(f"Goto status is normally {self.goto_status}")
        self.go_to_folder_window.clear()
        tree, item, f_name, _, abs_ = self.get_item_attrs()
        tree.clearSelection()
        tree.setCurrentItem(tree.topLevelItem(0))
        path_list = [i for i in dst.split('/') if i != '']
        if self.mode == 'Diags':
            self.goto_dst = (i for i in path_list)  # 生成器，路径
            self.dir_nandfs(dst)
            return
        # 在OS模式下删除所有子目录
        [tree.currentItem().takeChild(i) for i in range(tree.currentItem().childCount()-1, -1, -1)]
        for sub in path_list[:]:  # 遍历分割后的路径列表
            logger.info(f"Going to {sub}")
            tree, item, f_name, _, abs_ = self.get_item_attrs()
            sub_dirs = self.get_sub_path(self.get_abs_item_text(item, f_name))  # 获取当前item子列表
            if not sub_dirs and self.mode == 'OS':
                self.goto_status = 0 if self.goto_status else logger.warning(f"Goto status is normally {self.goto_status}")
                self.note_box.warning(f"目录不存在: {dst}")
                return
            elif not sub_dirs and self.mode == 'Diags':
                # logger.info(f"Going to {abs_}")
                # continue
                # self.note_box.warning(f"Diags mode is not currently support this feature")
                self.goto_dst = (i for i in path_list)  # 生成器，路径
                return
            else:
                self.set_sub_items(sub_dirs, 134)
                item.setExpanded(True) if not item.isExpanded() else ''
                # tree.clearSelection()
            # logger.debug(f"item {f_name} have child count : {item.childCount()}")
            for index in range(item.childCount()):  # 根据名字找到item
                if item.child(index).text(0) == sub:
                    tree.setCurrentItem(item.child(index))
                    break
        else:
            tree, item, f_name, _, abs_ = self.get_item_attrs()
            logger.debug(f"current : {f_name}, path: {abs_}")
            sub_dirs = self.get_sub_path(abs_)  # 获取当前item子列表
            if not sub_dirs:
                self.note_box.warning(f"sysdiagnose文件夹不存在")
            else:
                self.set_sub_items(sub_dirs, 149)
            self.goto_status = 0 if self.goto_status else logger.warning(f"Goto status is normally {self.goto_status}")
        item.setExpanded(True) if not item.isExpanded() else ''
        return True

    def get_item_attrs(self):
        """
        获取当前点击到项的 控件, 项, 文件名，文件类型，绝对路径
        :return: file_name, file_or_directory, abs_path
        """
        tree: QTreeWidget = self.findChild(QTreeWidget, 'tree')
        item = tree.currentItem()
        file_name = item.text(0)
        f_od_d = item.text(3)
        abs_ = self.get_abs_item_text(item, file_name)  # 获取device端的绝对路径
        return tree, item, file_name, f_od_d, abs_

    def get_sysdiagnose(self):
        """获取sysdiagnose保存到Mac上指定路径"""
        if self.mode == 'OS':
            self.goto(SYSD_PATH)
            self.save_to()
            # cmd = f"ls {SYSD_PATH} | egrep -c ."
            # r = self.ssh.run_cmd(cmd)
            # if r.get('color') == 'green' and intable(r.get('result')):
            #     if r == '1':
            #         file_name = self.ssh.run_cmd(f"ls {SYSD_PATH}").get('green', '')
            #         local_fp = QFileDialog.getExistingDirectory(self, 'Save file to',
            #                                                     os.path.expanduser(f'~/Desktop/{file_name}'),
            #                                                     options=QFileDialog.ShowDirsOnly)
            #     elif int(r) >= 2:
            #         pass
            #     else:
            #         logger.warning(f"CMD - {repr(cmd)} | RESULT - {repr(r)}")
        elif self.mode == 'Diags':
            self.goto(SYSD_PATH)
            self.save_to()
        else:
            logger.warning(f"Current mode: {self.mode}")

    def get_astro(self):
        if self.mode == 'OS':
            r = self.goto(Astro_PATH)
            if r:
                self.save_to()
        elif self.mode == 'Diags':
            self.goto(Astro_PATH)
            # self.save_to()
        else:
            logger.warning(f"Current mode: {self.mode}")

    def save_to(self):
        """
        fp : Directory path
        fp_: file_path
        f_ : file object
        :return:
        """
        _, _, file_name, f_od_d, src = self.get_item_attrs()
        # fp是个文件夹
        fp = QFileDialog.getExistingDirectory(self, 'Save file to', os.path.expanduser(f'~/Desktop/{file_name}'),
                                              options=QFileDialog.ShowDirsOnly)
        logger.debug(f"fp: {fp}")
        _, _, file_name, f_od_d, src = self.get_item_attrs()
        if fp:
            logger.debug(f"src path: {src}")
            # r = os.popen(f'ditto {src} {repr(fp_)}').read()  # from mac to mac
            # logger.info(f"Save file from {src} to {repr(fp_)} result: {r}")
            if self.mode == 'OS':
                # self.ssh.getf(src, fp+f'/{file_name}') if f_od_d == '-' else self.ssh.getd(src, fp)
                self.ssh2 = SSH(self.port_offset, self)
                getf_task = {'method': 'getf', 'src':src, 'dst': fp+f'/{file_name}'}
                getd_task = {'method': 'getd', 'src':src, 'dst': fp}
                self.ssh2.task = getf_task if f_od_d == '-' else getd_task
                self.ssh2.start()
                logger.debug(f"Saving file {src} to {repr(fp)}")
                # self.note_box.info(f"Already copy {os.path.basename(src)} to {fp}")
            elif self.mode == 'Diags':
                self.run_usbfs(des=fp)
                self.run_usbfs_dev('tomac', f_od_d, src=src)
                self.note_box.info(f"Coping {os.path.basename(src)} to {fp}")
            else:
                logger.error(f"Error: self.mode = {self.mode}")
        else:
            logger.error(f"Error fp: {repr(fp)}")

    def insert_file(self):
        _, _, file_name, f_od_d, dst = self.get_item_attrs()
        if intable(f_od_d) or f_od_d == '-':  # 如果点击的是文件，则获取其文件夹路径
            logger.debug(f"Change dst from {dst} to {os.path.dirname(dst)}")
            dst = os.path.dirname(dst)
        src_file_tuple = QFileDialog.getOpenFileName(self, 'Put file to', os.path.expanduser(f'~/Desktop/'))
        if len(src_file_tuple) == 2:
            src_file = src_file_tuple[0]
            dir_, src_ = os.path.split(src_file)
            if src_file:
                logger.debug(f"File path: {repr(src_file)}")
                if self.mode == 'OS':
                    # self.ssh.putf(src_file, dst)
                    self.ssh2 = SSH(self.port_offset, self)
                    self.ssh2.task = {'method': 'putf', 'src': src_file, 'dst': dst}
                    self.ssh2.start()
                    # self.note_box.info(f"Already copy {os.path.basename(src_file)} to {dst}")
                elif self.mode == 'Diags':
                    self.run_usbfs(des=dir_)  # 在src文件的文件夹开启usbfs
                    self.run_usbfs_dev('frommac', f_od_d, src=src_, dst=dst)
                    self.note_box.info(f"Coping {os.path.basename(src_file)} to {dst}")
                else:
                    logger.error(f"Error: self.mode = {repr(self.mode)}")
            else:
                logger.error(f"Error file path: {repr(src_file)}")
        else:
            logger.error(f"Error file path: {repr(src_file_tuple)}")

    def insert_folder(self):
        _, _, file_name, f_od_d, dst = self.get_item_attrs()
        if intable(f_od_d) or f_od_d == '-':  # 如果点击的是文件，则获取其文件夹路径
            logger.debug(f"Change dst from {dst} to {os.path.dirname(dst)}")
            dst = os.path.dirname(dst)
        src_folder = QFileDialog.getExistingDirectory(self, 'Put file to', os.path.expanduser(f'~/Desktop/'))
        logger.debug(f"Folder path: {repr(src_folder)}")
        if src_folder:
            dir_, src_ = os.path.split(src_folder)
            if self.mode == 'OS':
                # self.ssh.putd(src_folder, dst)
                self.ssh2 = SSH(self.port_offset, self)
                self.ssh2.task = {'method': 'putd', 'src': src_folder, 'dst': dst}
                self.ssh2.start()
                # self.note_box.info(f"Already copy {os.path.basename(src_folder)} to {dst}")
            elif self.mode == 'Diags':
                self.run_usbfs(des=dir_)  # 在src文件夹的文件夹开启usbfs
                self.run_usbfs_dev('frommac', f_od_d, src=src_, dst=dst)
                self.note_box.info(f"Coping {os.path.basename(src_folder)} to {dst}")
            else:
                logger.error(f"Error: self.mode = {self.mode}")
        else:
            logger.error(f"Error file path: {repr(src_folder)}")

    def del_file_folder(self):
        _, _, file_name, f_od_d, dst = self.get_item_attrs()

        if self.mode == 'OS':
            if f_od_d == '-':
                cmd = f'rm -f {dst}'
            else:
                cmd = f"rm -rf {dst}"
            self.ssh.run_cmd(cmd)
        elif self.mode == 'Diags':
            dst = "nandfs:" + dst.replace('/', '\\')  # Diags下的路径需要更改格式
            logger.debug(f"fd: {f_od_d} dst: {dst}")
            if intable(f_od_d):
                cmd = f"rm -f {dst}"
            else:
                cmd = ''
                self.note_box.warning(f"Delete folder is only supported in OS mode")
                return
                # pass  # todo Diags下删除文件夹要先遍历并删除其中的文件，最后删除文件夹
            self.parent.send_cmd_signal.emit(cmd+'\n')
        else:
            self.note_box.warning(f"Error mode: {self.mode}")

    def run_usbfs(self, des: str):
        """run usbfs on MacOS X"""
        logger.debug(f'Local usbfs: {des}')
        cont_path = os.path.dirname(os.path.dirname(sys.argv[0]))
        usbfs_path = os.path.join(tool_path, 'usbfs')
        assert os.path.exists(usbfs_path), f"{usbfs_path} not exist"
        if self.usbfs_running == 1:
            os.popen('killall usbfs')
            self.usbfs_running = 0
        command = f'set timeout -1;spawn "{usbfs_path}" -f "{des}";'+'expect "Disconnected";exit'

        # child = subprocess.Popen(["bash", "-c", command], close_fds=True)
        with open(os.path.join(log_folder, 'usbfs.log'), 'a') as f:
            child = subprocess.Popen(['/usr/bin/expect', '-c', command], close_fds=True, stdout=f, stderr=f)
        time.sleep(0.5)

        self.usbfs_running = 1 if not self.usbfs_running else 0  # 修改运行状态为1
        assert self.usbfs_running == 1
        # logger.info(f'{usbfs_path} -f {des}')
        logger.info(
            f"SPAWN usbfs - CMD: [{command}] \nOUTPUT: [{child.stdout}] \nError: [{child.stderr}] \nRC: [{child.returncode}]")

    def run_usbfs_dev(self, mode: str, file_type: str, **kwargs):
        """

        :param src: source
        :param dst: destination
        :param mode: 'frommac' or 'tomac'
        :param file_type:
            In Diags mode:
                <SYMLINK> or <DIR> means file type is folder
                intable() means file type is file
            In OS mode:
                d means directory
                - means file
        :return:
        """

        if mode == 'frommac':  # 根据模式设定src和dst
            src = 'usbfs:\\' + kwargs.get('src', '')
            dst = 'nandfs:\\' + kwargs.get('dst', '').replace('/', '\\')
        elif mode == 'tomac':
            src = 'nandfs:\\' + kwargs.get('src', '').replace('/', '\\')
            dst = 'usbfs:\\'
        else:
            logger.error(f'Error mode {repr(mode)} not in "frommac" or "tomac"')
            return
        # if file_type in ['<DIR>', '<SYMLINK>']:  # 根据要传送的文件类型设置_option
        #     _option = ' -r'
        # elif intable(file_type.replace(',', '_')):
        #     _option = ' -r'
        # else:
        #     logger.error(f"Error file type {repr(file_type)}")
        #     return
        cmd = f"\nusbfs -m;cp -r {src} {dst};usbfs -u"
        logger.info(f"Copying {file_type} {src} {mode} {dst}")
        self.parent.send_cmd_signal.emit(f'{cmd}\n')

    def click_timer_timeout(self):
        self.click_timer.stop()
        if self.click_count == 1:
            self.item_clicked()
        elif self.click_count > 1:
            self.item_doub_clicked()
        else:
            logger.error(f"click count = {self.click_count}")
        self.click_count = 0

    def item_click_timer(self, item: QTreeWidgetItem, column: int):
        if not self.click_timer.isActive():
            self.click_timer.start(self.delay * 3)
        self.click_count += 1

    def item_clicked(self):
        tree: QTreeWidget = self.findChild(QTreeWidget, 'tree')
        item = tree.currentItem()
        p = item.text(0)
        # print(tree.itemAbove(item).text(0))
        _path = self.get_abs_item_text(item, p)
        # dirs = self.ssh.lsdir(_path)

    def item_doub_clicked(self):
        _, item, p, item_type, _ = self.get_item_attrs()
        if item_type == '-':
            self.note_box.warning(f"{p} 是文件，不是文件夹")
            return
        sub_dirs = self.get_sub_path(self.get_abs_item_text(item, p))  # 获取自文件夹/文件信息
        if not sub_dirs:
            logger.debug(f"No subdirs returned.")
            return
        else:
            self.set_sub_items(sub_dirs, 348)
        # item.setExpanded(True) if not item.isExpanded() else ''

    def set_sub_items(self, sub_dirs: list, line_no: int = 0):
        """在treeview中设置子文件(夹)"""
        logger.debug(f"Setting subdirs data{', line no: '+repr(line_no) if line_no else ''}")
        tree, item, f_name, _, _ = self.get_item_attrs()
        if item.childCount():
            logger.debug(f"current item {f_name} has {item.childCount()} children.")
            for _index in range(item.childCount(), 0, -1):  # 删了0，就会少了-1，导致只能删除一半，所以倒序删除
                # logger.debug(f"Removing child index {_index}")
                item.takeChild(_index-1)  # index 从0起，所以要-1
        else:
            logger.debug(f"current item {f_name} has no child.")

        logger.debug(f"Current mode: {repr(self.mode)} id: {id(self.mode)}")
        try:
            if self.mode == 'OS':
                logger.debug(f"Setting OS subdirs")
                for sub_dir_ in sub_dirs:
                    type_, priviledge_, size, month, day, time_or_year, name_ = sub_dir_
                    item_ = QTreeWidgetItem([name_, size, f"{month} {day} {time_or_year}", type_, priviledge_])
                    if type_ in 'd':
                        item_.setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_DirClosedIcon)))
                    elif type_ == 'l':
                        item_.setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_FileLinkIcon)))
                    elif type_ == '-':
                        item_.setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_FileIcon)))
                    else:
                        logger.warning(f"type: {type_}")
                    item.addChild(item_)
            else:
                logger.debug(f"Setting Diags subdirs")
                for sub_dir_ in sub_dirs:
                    # -------------- N15x iEFI1 ---------------
                    # priviledge_, type_, time_, name_ = sub_dir_
                    # item_ = QTreeWidgetItem(
                    #     [name_, type_ if intable(type_.replace(',', '_')) else '', time_, type_, priviledge_])
                    # if type_ in ["<SYMLINK>", "<DIR>"]:  # type = folder or symlink
                    #     item_.setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_DirClosedIcon)))
                    # elif intable(type_.replace(',', '_')):  # type = file
                    #     item_.setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_FileIcon)))
                    # else:
                    #     logger.warning(f"type: {type_}")

                    # -------------- N18x iEFI2 ---------------
                    datetime_, type_, size_, name_ = sub_dir_
                    type_ = type_ or '1'
                    item_ = QTreeWidgetItem(
                        [name_, '-' if intable(type_) else type_, datetime_, '-' if intable(type_) else type_, ''])
                    if type_ in ["<SYMLINK>", "<DIR>"]:  # type = folder or symlink
                        item_.setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_DirClosedIcon)))
                    elif intable(type_):  # type = file
                        item_.setIcon(0, QIcon(self.style().standardPixmap(QStyle.SP_FileIcon)))
                    else:
                        logger.warning(f"type: {type_}")

                    item.addChild(item_)
        except Exception as e:
            logger.error(e, exc_info=1)
        logger.debug(f"Ending set sub items")
        item.setExpanded(True) if not item.isExpanded() else ''

    def resizeEvent(self, a0) -> None:
        self.findChild(QTreeWidget, 'tree').resize(self.size())
        super(TreeView, self).resizeEvent(a0)

    def get_abs_item_text(self, item: QTreeWidgetItem, p: str):
        """get absolute path from parents"""
        # p = p.split(' -> ')[0]  # 获取符号链接的真实name
        above_item = item.parent()
        if above_item:
            p = os.path.join(above_item.text(0), p)
            return self.get_abs_item_text(above_item, p)
        else:
            return p

    def get_sub_path(self, cur_path: str):
        """get sub dirs in OS mode"""
        # sub = os.popen(f'ls -l {cur_path}').readlines()
        sub = None
        if self.mode == 'OS':
            try:
                sub = self.ls_dir(cur_path).split('\n')
            except AttributeError as e:
                logger.warning(e)
                return
        elif self.mode == 'Diags':
            self.dir_nandfs(cur_path)  # run cmd dir nandfs:\\cur_path
            return
        if not sub:
            return
        pts = []
        for sub_ in sub:
            m = pat_os.findall(pat_replace_link.sub('', sub_))
            pts = chain(pts, m)
        return pts

    def ls_dir(self, cur_path: str):
        """run ls -lt cmd in OS mode"""
        r = self.ssh.run_cmd(f"ls -l {repr(cur_path)}/")
        if r.get('color') == 'green':
            res = r.get('result')
            logger.debug(repr(res))
            return res
        elif r.get('color') == 'red':
            res = r.get('result')
            logger.error(repr(res))
            self.note_box.warning(res)
            return None
            # quit()

    def dir_nandfs(self, cur_path: str = ''):
        """send 'dir nandfs:\\' cmd and switch dir status to 1"""
        logger.debug(f"Goto dir {cur_path}") if cur_path else ''
        if self.goto_status:
            tree, item, f_name, _, abs_ = self.get_item_attrs()
            if tree.currentItem().isExpanded():
                logger.debug(f"current item {f_name} is expanded.")
                try:
                    next_dir = next(self.goto_dst)
                    logger.debug(f"在{repr(abs_)}下找{repr(next_dir)}")
                    # 根据名字找到item
                    # for index in range(item.childCount()):
                    #     item_text = item.child(index).text(0)
                    #     logger.debug(f"index: {index} {item_text} ???")
                    #     if item_text == next_dir:
                    #         tree.setCurrentItem(item.child(index))
                    #         logger.debug(f"Selected {tree.currentItem().text(0)}")
                    #         break
                    # else:
                    #     self.note_box.warning(f"没有找到{next_dir}")
                    #     self.goto_status = 0  # 没有找到下级目录，停止goto
                    index = self.get_subdir_index(next_dir)
                    if index is not None:
                        tree.setCurrentItem(item.child(index))
                        logger.debug(f"Selected {tree.currentItem().text(0)}")
                    else:
                        self.note_box.warning(f"没有找到{next_dir}")
                        self.goto_status = 0  # 没有找到下级目录，停止goto

                except StopIteration:
                    self.goto_status = 0  # 完成遍历，设置
                    return
                tree, item, f_name, _, abs_ = self.get_item_attrs()
            cur_path = abs_.replace('/', '\\')
            self.parent.send_cmd_signal.emit(f"dir nandfs:\\{cur_path}\n")
            self.parent.switch_dir_status(551)
            pass
        else:
            cur_path = cur_path.replace('/', '\\')
            self.parent.send_cmd_signal.emit(f"dir nandfs:\\{cur_path}\n")
            self.parent.switch_dir_status(556)

    @property
    def dir_list(self):
        return self._dir_list

    @dir_list.setter
    def dir_list(self, v: str):
        if v == '->end<-':
            logger.debug(f"Ended 'dir' cmd, parsing diags dirs...")
            self.parse_diags_dirs()  # got dir list then parse it
        else:
            self._dir_list.append(v)

    def set_dir_list(self, v: str):
        self.dir_list = v

    def parse_diags_dirs(self):
        """从dir nandfs:\\的output中解析出目录信息"""
        pts = []
        logger.info(f"dir_list: {self.dir_list}")
        for _dir in self.dir_list:
            m = pat_diags_n18x.findall(pat_replace_link.sub('', _dir))
            pts = chain(pts, m) if m else pts
        else:  # 用完清空
            self._dir_list = []
        self.set_sub_items(pts, 581)
        if self.goto_status:
            self.dir_nandfs()

    def get_subdir_index(self, subdir_name: str=''):
        tree, item, f_name, _, abs_ = self.get_item_attrs()
        _count = item.childCount()
        logger.debug(f"item {item.text(0)} child count: {_count}")
        for i in range(_count):
            _text = item.child(i).text(0)
            logger.debug(f"index: {i} {_text}")
            if _text == subdir_name:
                return i
        else:
            return

    @property
    def goto_status(self):
        return self._goto_status

    @goto_status.setter
    def goto_status(self, v: int):
        logger.debug(f"goto_status {self.goto_status} modify to {v}")
        self._goto_status = v

    @pyqtSlot(dict)
    def note_box_(self, v: dict):
        """v={m: info/warning, c: content}"""
        getattr(self.note_box, v.get('m'))(v.get('c'))

    def closeEvent(self, a0) -> None:
        self.close_signal.emit(self.objectName())
        os.popen('killall usbfs') if self.usbfs_running == 1 else ''
        super(TreeView, self).closeEvent(a0)


def get_usbfs_pid():
    check = "ps -e -opid -ocommand | grep usbfs | grep -v grep"
    r = os.popen(check).read()
    regex = re.match(r"\s*(?P<pid>\d+)", r)
    pid = int(regex.group('pid'))
    logger.info(f"CHECK USBFS PID - CMD: {check} OUTPUT: {r} PID: {pid}")
    if pid:
        return pid


def intable(i: str):
    try:
        int(i.replace(',', ''))
        return True
    except ValueError:
        return False


class USBFSThread(QThread):
    def __init__(self):
        super(USBFSThread, self).__init__()

    def run(self) -> None:
        pass


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
    tv = TreeView()
    tv.show()
    app.exec_()
